﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using Dycox.Utils;

namespace Dycox.Reporting
{
    [Serializable]
    public abstract class Style : ICloneable
    {
        private NameValueCollection _values;
        private Style[] _children;

        public Style()
            : this(0)
        {
        }

        protected Style(int childCount)
        {
            if (childCount > 0)
                _children = new Style[childCount];
        }

        protected Style(Style[] childStyls)
        {
            if (childStyls != null && childStyls.Length > 0)
                _children = childStyls.ToArray();
        }

        protected T GetChild<T>(int index, bool create)
            where T: Style, new()
        {
            if (_children == null || index < 0 || index >= _children.Length)
                throw new IndexOutOfRangeException();

            Style style = _children[index];
            if (style == null && create)
            {
                style = new T();
                _children[index] = style;
            }
            return (T)style;
        }

        protected bool Specified(string key)
        {
            if (_values != null)
                return _values.Get(key) != null;
            else
                return false;
        }

        protected T GetValueOrDefault<T>(string key)
        {
            return GetValueOrDefault(key, default(T));
        }

        protected T GetValueOrDefault<T>(string key, T defaultValue)
        {
            if (_values != null)
            {
                string text = _values[key];

                if (text != null)
                    return ConvertUtil.ConvertType<T>(text);
            }
            
            return defaultValue;
        }

        protected void SetValue(string key, object value)
        {
            string raw;

            if (value == null) raw = null;
            else raw = ConvertUtil.ConvertType<string>(value);

            SetValueCore(key, raw);
        }

        internal void SetValueCore(string key, string value)
        {
            if (value == null)
            {
                if (_values != null)
                    _values.Remove(key);
            }
            else
            {
                if (_values == null)
                    _values = new NameValueCollection();
                _values[key] = value;
            }
        }

        public virtual Style Clone()
        {
            Style other = (Style)this.MemberwiseClone();

            if (_values != null)
                other._values = new NameValueCollection(_values);


            if (_children != null)
            {
                int length = _children.Length;
                Style[] array = new Style[length];
                for (int i = 0; i < length; i++)
                    array[i] = _children[i].Clone();
                other._children = array;
            }

            return other;
        }

        object ICloneable.Clone()
        {
            return this.Clone();
        }

        /// <summary>
        /// Override current settings by another <see cref="Style"/> object.
        /// </summary>
        /// <param name="other">The <see cref="Style"/> instance that we want current object to be overriden by.</param>
        /// <exception cref="ArgumentNullException"><paramref name="other"/> is null.</exception>
        internal virtual void OverrideBy(Style other)
        {
            if (other == null)
                throw new ArgumentNullException("other");
            if (other.GetType() != this.GetType())
                throw new ArgumentException("Type not equal.");

            NameValueCollection coll = other._values;

            if (coll != null)
            {
                foreach (string key in coll.AllKeys)
                {
                    if (!this.Specified(key))
                        this.SetValueCore(key, coll[key]);
                }
            }

            Style[] children = other._children;
            if (children != null)
            {
                Style child0, child1;
                int length = children.Length;

                for (int i = 0; i < length; i++)
                {
                    child1 = children[i];

                    if (child1 != null)
                    {
                        child0 = _children[i];
                        if (child0 == null)
                        {
                            child0 = child1.Clone();
                            _children[i] = child0;
                        }
                        else
                            child0.OverrideBy(child1);
                    }
                }
            }
        }

    }
}
